//------------------------------------------------------------------------------------------------------
//   _____     _______ ____  _     ___  ____  ____    _____ _   _  ____ ___ _   _ _____   ______  ___ _ 
//  / _ \ \   / / ____|  _ \| |   / _ \|  _ \|  _ \  | ____| \ | |/ ___|_ _| \ | | ____| |  _ \ \/ / / |
// | | | \ \ / /|  _| | |_) | |  | | | | |_) | | | | |  _| |  \| | |  _ | ||  \| |  _|   | | | \  /| | |
// | |_| |\ V / | |___|  _ <| |__| |_| |  _ <| |_| | | |___| |\  | |_| || || |\  | |___  | |_| /  \| | |
//  \___/  \_/  |_____|_| \_\_____\___/|_| \_\____/  |_____|_| \_|\____|___|_| \_|_____| |____/_/\_\_|_|
//
// Overlord Engine v1.113
// Copyright Overlord Thomas Goussaert & Overlord Brecht Kets
// http://www.digitalartsandentertainment.com/
//------------------------------------------------------------------------------------------------------
//Precompiled Header [ALWAYS ON TOP IN CPP]
#include "stdafx.h"

#include "ModelAnimator.h"
#include "../Diagnostics/Logger.h"


ModelAnimator::ModelAnimator(MeshFilter* pMeshFilter):
m_pMeshFilter(pMeshFilter),
m_Transforms(vector<XMFLOAT4X4>()),
m_IsPlaying(false), 
m_Reversed(false),
m_ClipSet(false),
m_TickCount(0),
m_AnimationSpeed(1.0f)
{
	SetAnimation(0);
}


ModelAnimator::~ModelAnimator()
{
}

void ModelAnimator::SetAnimation(UINT clipNumber)
{
	//Set m_ClipSet to false
	m_ClipSet = false;
	//Check if clipNumber is smaller than the actual m_AnimationClips vector size
	if (!(clipNumber < m_pMeshFilter->m_AnimationClips.size()))
	{
		Reset();
		Logger::LogWarning(L"The amount of clips is bigger than de amount of animation clips");
	}
	else
	{
		SetAnimation(m_pMeshFilter->m_AnimationClips[clipNumber]);
	}
	//If not,
	//	Call Reset
	//	Log a warning with an appropriate message
	//	return
	//else
	//	Retrieve the AnimationClip from the m_AnimationClips vector based on the given clipNumber
	//	Call SetAnimation(AnimationClip clip)
}

void ModelAnimator::SetAnimation(wstring clipName)
{
	//Set m_ClipSet to false
	m_ClipSet = false;
	//Iterate the m_AnimationClips vector and search for an AnimationClip with the given name (clipName)
	for (unsigned int i = 0; i < m_pMeshFilter->m_AnimationClips.size(); ++i)
	{
		if (m_pMeshFilter->m_AnimationClips[i].Name == clipName)
		{
			SetAnimation(m_pMeshFilter->m_AnimationClips[i]);
			return;
		}
	}

	Reset();
	Logger::LogWarning(L"Animations Name was not found");
	//If found,
	//	Call SetAnimation(Animation Clip) with the found clip
	//Else
	//	Call Reset
	//	Log a warning with an appropriate message
}

void ModelAnimator::SetAnimation(AnimationClip clip)
{
	//Set m_ClipSet to true
	m_ClipSet = true;
	//Set m_CurrentClip
	m_CurrentClip = clip;
	//Call Reset(false)
	Reset(false);
}

void ModelAnimator::Reset(bool pause)
{
	//If pause is true, set m_IsPlaying to false
	//Set m_TickCount to zero
	//Set m_AnimationSpeed to 1.0f
	if (pause)
	{
		m_IsPlaying = false;
	}

	m_TickCount = 0;
	m_AnimationSpeed = 1.0f;

	//If m_ClipSet is true
	//	Retrieve the BoneTransform from the first Key from the current clip (m_CurrentClip)
	//	Refill the m_Transforms vector with the new BoneTransforms (have a look at vector::assign)
	//Else
	//	Create an IdentityMatrix 
	//	Refill the m_Transforms vector with this IdenityMatrix (Amount = BoneCount) (have a look at vector::assign)
	if (m_ClipSet == true)
	{
		auto boneTransform = m_CurrentClip.Keys[0].BoneTransforms;
		m_Transforms.assign(boneTransform.begin(), boneTransform.end());
	}
	else
	{
		XMMATRIX idMatrix = XMMatrixIdentity();
		XMFLOAT4X4 idMatrix4x4;
		XMStoreFloat4x4(&idMatrix4x4, idMatrix);
		m_Transforms.assign(m_pMeshFilter->m_BoneCount, idMatrix4x4);
	}
}

void ModelAnimator::Update(const GameContext& gameContext)
{
	//We only update the transforms if the animation is running and the clip is set
	if (m_IsPlaying && m_ClipSet)
	{
		//1. 
		//Calculate the passedTicks (see the lab document)
		float elapsedSeconds = gameContext.pGameTime->GetElapsed();
		float ticksPerSecond = m_pMeshFilter->m_AnimationClips[0].TicksPerSecond;
		float passedTicks = elapsedSeconds * ticksPerSecond * m_AnimationSpeed;
		//Make sure that the passedTicks stay between the m_CurrentClip.Duration bounds (fmod)
		passedTicks = fmod(m_CurrentClip.Duration, passedTicks);
		//2. 
		//IF m_Reversed is true
		//	Subtract passedTicks from m_TickCount
		//	If m_TickCount is smaller than zero, add m_CurrentClip.Duration to m_TickCount
		//ELSE
		//	Add passedTicks to m_TickCount
		//	if m_TickCount is bigger than the clip duration, subtract the duration from m_TickCount
		if (m_Reversed == true)
		{
			m_TickCount -= passedTicks;
			if (m_TickCount < 0)
			{
				m_TickCount += m_CurrentClip.Duration;
			}
		}
		else
		{
			m_TickCount += passedTicks;
			if (m_TickCount > m_CurrentClip.Duration)
			{
				m_TickCount -= m_CurrentClip.Duration;
			}
		}

		//3.
		//Find the enclosing keys
		AnimationKey keyA, keyB;
		//Iterate all the keys of the clip and find the following keys:
		//keyA > Closest Key with Tick before/smaller than m_TickCount
		//keyB > Closest Key with Tick after/bigger than m_TickCount
		keyA = m_CurrentClip.Keys[0];
		keyB = m_CurrentClip.Keys[m_CurrentClip.Keys.size() - 1];

		for ( unsigned int i = 0; i < m_CurrentClip.Keys.size(); ++i)
		{
			if (m_CurrentClip.Keys[i].Tick < m_TickCount && m_CurrentClip.Keys[i].Tick > keyA.Tick)
			{
				keyA = m_CurrentClip.Keys[i];
			}

			if (m_CurrentClip.Keys[i].Tick > m_TickCount && m_CurrentClip.Keys[i].Tick < keyB.Tick)
			{
				keyB = m_CurrentClip.Keys[i];
			}
		}

		//4.
		//Interpolate between keys
		//Figure out the BlendFactor (See lab document)
		float blendFactor = (m_TickCount - keyA.Tick)/(keyB.Tick - keyA.Tick);
		//Clear the m_Transforms vector
		//FOR every boneTransform in a key (So for every bone)
		for ( unsigned int i = 0; i < keyA.BoneTransforms.size(); ++i)
		{
			auto transformA = keyA.BoneTransforms[i];
			auto transformB = keyB.BoneTransforms[i];
			XMVECTOR translateA;
			XMVECTOR rotateA;
			XMVECTOR scaleA;
			XMMATRIX toMatrixA = XMLoadFloat4x4(&transformA);
			XMMatrixDecompose(&scaleA, &rotateA, &translateA, toMatrixA);

			XMVECTOR translateB;
			XMVECTOR rotateB;
			XMVECTOR scaleB;
			XMMATRIX toMatrixB = XMLoadFloat4x4(&transformB);
			XMMatrixDecompose(&scaleB, &rotateB, &translateB, toMatrixB);

			auto lerpTransform = XMVectorLerp(translateA, translateB, blendFactor);
			auto lerpScale = XMVectorLerp(scaleA, scaleB, blendFactor);
			auto lerpRotate = XMQuaternionSlerp (rotateA, rotateB, blendFactor);

			XMVECTOR id = XMVECTOR();

			XMMATRIX m = XMMatrixTransformation(id, id, lerpScale, id, lerpRotate, lerpTransform);
			XMFLOAT4X4 storeMatrix;
			XMStoreFloat4x4(&storeMatrix, m);
			m_Transforms[i] = storeMatrix;
			
		}
		//	Retrieve the transform from keyA (transformA)
		//	auto transformA = ...
		// 	Retrieve the transform from keyB (transformB)
		//	auto transformB = ...
		//	Decompose both transforms
		//	Lerp between all the transformations (Position, Scale, Rotation)
		//	Compose a transformation matrix with the lerp-results
		//	Add the resulting matrix to the m_Transforms vector
	}
}
